const nativeIsArray = Array.isArray;
/**
 * 对象是否包含制定的键
 * @param {Object} obj 
 * @param {string} key 
 */
export const has = function(obj, key) {
    return obj != null && hasOwnProperty.call(obj, key);
};
/**
 * 判断是否为字符串
 * @param {string} obj 
 */
export const isString = function(obj) {
    return toString.call(obj) === '[object String]';
}
/**
 * 判断是否为数组
 */
export const isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
};
/**
 * 如果object 不包含任何值(没有可枚举的属性)，返回true。
 * @param {*} obj 
 */
export const isEmpty = function(obj) {
    if (obj == null) return true;
    if (isArray(obj) || isString(obj)) return obj.length === 0;
    for (var key in obj) if (has(obj, key)) return false;
    return true;
};
/**
 * 获取当前时间戳
 */
const now = Date.now || function() {
    return new Date().getTime();
};
/**
   * 创建并返回一个像节流阀一样的函数
   * @param {Funtion} func 传入的函数
   * @param {Number} wait 时间间隔 
   * @param {leading: false || trailing: false} options 
*/
  export function throttle(func, wait, options) {
      let context, args, result;
      let timeout = null;
      let previous = 0;
      if (!options) options = {};
      const later = function() {
        previous = options.leading === false ? 0 : now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      };
      return function() {
        if (!previous && options.leading === false) previous = now();
        let remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
          if (timeout) {
            clearTimeout(timeout);
            timeout = null;
          }
          previous = now;
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
          timeout = setTimeout(later, remaining);
        }
        return result;
      };
    }
/**
 * 函数防抖
 * @param {Function} func 返回 function 函数的防反跳版本,
 * @param {Number} wait 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后.
 * @param {Boolean} immediate 为 true， debounce会在 wait 时间间隔的开始调用这个函数 
 */
 export function debounce(func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        var later = function() {
          var last = now() - timestamp;
          if (last < wait && last >= 0) {
            timeout = setTimeout(later, wait - last);
          } else {
            timeout = null;
            if (!immediate) {
              result = func.apply(context, args);
              if (!timeout) context = args = null;
            }
          }
        };
    
        return function() {
          context = this;
          args = arguments;
          timestamp = now();
          var callNow = immediate && !timeout;
          if (!timeout) timeout = setTimeout(later, wait);
          if (callNow) {
            result = func.apply(context, args);
            context = args = null;
          }
    
          return result;
        };
};


/**
 * 下载批量导入模板
 * @param {String} url 导出模板地址
 */
export function downloadBatchDemo(url) {
    let form = document.createElement("form");

    form.action = url;
    form.method = 'get';
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
}

/**
 * 长时间的信息提示
 */
export function longTimeMessage(self, res, time) {
    let msg = '';

    if(typeof(res.msg) == 'object') {
        for (let i = 0; i <= res.msg.length - 1; i++) {
            msg += `<p style="line-height:20px;color:#f56c6c">${res.msg[i]}</p>`;
        }
    } else {
        msg = res.msg;
    }

    self.messageObj = self.$message({
        duration: time,
        type: 'warning',
        dangerouslyUseHTMLString: true,
        message: msg
    });
}

/**
 * uploader 组件失败函数回调
 * @param {Object} self 当前页面引用
 * @param {Object} file 当前上传失败的数据
 * @param {String} message 上传失败返回的信息
 */
export function uploaderFileFail(self, uploderDom, file, message) {
    let mess = eval('(' + message + ')');

    if(+mess.error === 401 && self.uploadRetryTimes > 0){
        let callbcak = (res => {
            self.uploadRetryTimes--;
            uploderDom.retry(file);
        })

        self.get_access_token(callbcak);
    }else{
        self.$message.error(mess.msg);
        uploderDom.removeFile(file);
    }
}

/**
 * 字符串转类型化数组
 * @param {String} str 字符串
 */
export function str2Buffer(str) {
    if (typeof ArrayBuffer !== 'undefined') {
        let buf = new ArrayBuffer(str.length);
        let view = new Uint8Array(buf);

        for (let i = 0; i != str.length; i+=1){
            view[i] = str.charCodeAt(i) & 0xFF;
        }
        return buf;
    } else {
        let buf = new Array(str.length);

        for (let i = 0; i != str.length; i+=1){
            buf[i] = str.charCodeAt(i) & 0xFF;
        }
        return buf;
    }
}

/**
 * base64转换为文件
 * @param {String} dataurl 图片信息
 * @param {String} filename 图片名字
 * @param {String} type 图片名字后缀转为png
 */
export function dataURLtoFile(dataurl, filename, type=undefined) {
    if (type === 'png') {
        filename = filename.split(/(.*)\.[^.]+/).join('') + '.png';
    }

    let arr = dataurl.split(',');
    let mime = arr[0].match(/:(.*?);/)[1];
    let bstr = atob(arr[1]);
    let n = bstr.length;
    let u8arr = new Uint8Array(n);

    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {
        type: mime
    });
}

/**
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 * @param {Object} source
 * @returns {Object}
 */
export function deepClone(source) {
    if (!source && typeof source !== 'object') {
      throw new Error('error arguments', 'deepClone')
    }
    const targetObj = source.constructor === Array ? [] : {}
    Object.keys(source).forEach(keys => {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = deepClone(source[keys])
      } else {
        targetObj[keys] = source[keys]
      }
    })
    return targetObj
  }
  